/**
    Copyright (c) 2010 ATTi 
    All rights reserved.
    Author: Michael Shu
    Date: 8/12/2010
*/

global class BatchUpdateAccountTeam implements Database.Batchable<sObject>, Database.Stateful{
	private static String CMRAgencyRecordTypeName = 'CMR Agency';
    private static String CMRAgencyClientRecordTypeName = 'CMR Agency Client';
    private final String query;
    private final String commandcode;
    private static String ALL  = 'All';
    private static String EDIT = 'Edit';
    private static String READ = 'Read';
    private static String NONE = 'None';
    private static Map<String, Integer> levels = new Map<String, Integer>{ALL => 3, EDIT => 2, READ => 1, NONE => 0, '' => -1};
    private static Set<String> ROW_REASONS = new Set<String>{'Team', 'Owner', 'Manual'};
    
    //----------------command code -------------------
    public static String CleanAccountTeam = 'CleanAccountTeam';
    public static String AggregateAccountTeam  = 'AggregateAccountTeam';
    public static String AddAndRemoveAccoutFollowersInChatter = 'AddAndRemoveAccoutFollowersInChatter';
    
    private String compareLevel(String perClient, String perAgency){
    	String rst =  (levels.get(perClient) > levels.get(perAgency))? perClient : perAgency;
    	if(rst == ALL){
			rst = EDIT;
		}
    	return rst;
    }
    
    global BatchUpdateAccountTeam(String cmd){
    	this.commandcode = cmd;
    	if(cmd == CleanAccountTeam){
    		this.query = ' select Id from AccountTeamMember where Account.Recordtype.Name =: CMRAgencyRecordTypeName ';
    	}else if(cmd == AggregateAccountTeam){
    		this.query = ' select AccountAccessLevel, AccountId, Account.Recordtype.Name, Account.Agency__c, Id, TeamMemberRole, UserId ' 
                       + ' from AccountTeamMember where Account.Recordtype.Name =: CMRAgencyClientRecordTypeName and Account.Agency__c != null and User.IsActive = true ';
    	}else if(cmd == AddAndRemoveAccoutFollowersInChatter){
    		this.query = ' select ID, OwnerId, Owner.isActive, (select id, UserId, TeamMemberRole from AccountTeamMembers where User.IsActive = true) ' 
                       + ' from Account where Recordtype.Name =: CMRAgencyClientRecordTypeName ';
    	}
    }
    
	global Database.Querylocator start(Database.BatchableContext bc){
        return Database.getQueryLocator(query);		
	}
	
	global void execute(Database.BatchableContext bc, List<sObject> scope){
		if(commandcode == CleanAccountTeam){
			try{
				delete scope;
			}catch(Exception ex){
				GeneralConstants.mailAnyExceptionToMe('BatchUpdateAccountTeam', 'delete account team Fail', ex);
			}
		}else if(commandcode == AggregateAccountTeam){
			System.debug('AggregateAccountTeam started');
			Map<String, AccountTeamMember> aggreatedAccteams = new Map<String, AccountTeamMember>(); 
			Set<ID> clientIds = new Set<ID>();
			Set<ID> agencyIds = new Set<ID>();
			Set<ID> userIds = new Set<ID>();
			Map<String, AccountShare> agencyShrsToUpsert = new Map<String, AccountShare>();
			Map<String, AccountShare> client_permissions = new Map<String, AccountShare>();
			Map<String, AccountShare> agency_permissions = new Map<String, AccountShare>();
			
			for(sObject s : scope){
				AccountTeamMember acc_tm =  (AccountTeamMember) s;
				clientIds.add(acc_tm.AccountId);
				userIds.add(acc_tm.UserId);
				agencyIds.add(acc_tm.Account.Agency__c);
			}
			List<AccountShare> accShrs = [Select AccountId, Account.Agency__c,  Account.Agency__r.OwnerId, AccountAccessLevel, CaseAccessLevel, OpportunityAccessLevel, UserOrGroupId 
			                              from AccountShare 
			                              where RowCause IN: ROW_REASONS 
			                                and ((AccountId IN: clientIds and UserOrGroupId IN: userIds and Account.Agency__c != null) or AccountId IN: agencyIds)];
			for(AccountShare a : accShrs){
				String acc_user_id = '' + a.AccountId + a.UserOrGroupId;
				if(clientIds.contains(a.AccountId)){
					client_permissions.put(acc_user_id, a);
				}
				if(agencyIds.contains(a.AccountId)){
					agency_permissions.put(acc_user_id, a);
					System.debug(' acc_user_id  = ' + acc_user_id);
				}
			}
			for(sObject s : scope){
				AccountTeamMember acc_tm =  (AccountTeamMember) s;
				AccountTeamMember a = new AccountTeamMember();
				a.AccountId = acc_tm.Account.Agency__c;
				a.TeamMemberRole = acc_tm.TeamMemberRole;
				a.UserId = acc_tm.UserId;
				aggreatedAccteams.put(''+ a.AccountId + a.UserId, a);
				
				String client_match = '' + acc_tm.AccountId + acc_tm.UserId;
				String agency_match = '' + acc_tm.Account.Agency__c + acc_tm.UserId;
				System.debug(' agency_match  = ' + agency_match);
				AccountShare client_shr = client_permissions.get(client_match);
				AccountShare agency_shr = agency_permissions.get(agency_match);
				System.debug(' agency_shr  = ' + client_permissions.get(agency_match));
				if(client_shr != null && client_shr.Account.Agency__r.OwnerId != client_shr.UserOrGroupId){
					if(agency_shr != null){
						agency_shr.AccountAccessLevel = compareLevel(client_shr.AccountAccessLevel, agency_shr.AccountAccessLevel);
						agency_shr.CaseAccessLevel    = compareLevel(client_shr.CaseAccessLevel, agency_shr.CaseAccessLevel);
						agency_shr.OpportunityAccessLevel = compareLevel(client_shr.OpportunityAccessLevel, agency_shr.OpportunityAccessLevel);
						agencyShrsToUpsert.put('' + agency_shr.AccountId + agency_shr.UserOrGroupId, agency_shr);
						System.debug(' agency_shr.AccountAccessLevel = ' + agency_shr.AccountAccessLevel);
						System.debug(' agency_shr.CaseAccessLevel = ' + agency_shr.CaseAccessLevel);
						System.debug(' agency_shr.OpportunityAccessLevel = ' + agency_shr.OpportunityAccessLevel);
					}else{
						AccountShare new_agency_shr = new AccountShare();
						new_agency_shr.AccountId = client_shr.Account.Agency__c;
						new_agency_shr.UserOrGroupId = client_shr.UserOrGroupId;
						new_agency_shr.AccountAccessLevel = compareLevel(client_shr.AccountAccessLevel, READ);
						new_agency_shr.CaseAccessLevel    = compareLevel(client_shr.CaseAccessLevel, None);
						new_agency_shr.OpportunityAccessLevel = compareLevel(client_shr.OpportunityAccessLevel, None);
						agencyShrsToUpsert.put('' + new_agency_shr.AccountId + new_agency_shr.UserOrGroupId, new_agency_shr);
					}
				}
			}
			try{
				insert aggreatedAccteams.values();
				upsert agencyShrsToUpsert.values();
			}catch(Exception ex){
				GeneralConstants.mailAnyExceptionToMe('BatchUpdateAccountTeam', 'insert account team Fail', ex);
			}
		}else if(commandcode == AddAndRemoveAccoutFollowersInChatter){ 
        	Map<ID, Set<ID>> accounts = new Map<ID, Set<ID>>();
        	for(sObject s : scope){
				Account a =  (Account) s;
				Set<ID> uids = new Set<ID>();
				if(a.Owner.isActive){
					uids.add(a.OwnerId);
				}
				for(AccountTeamMember member : a.AccountTeamMembers){
					if(member.TeamMemberRole.toLowerCase() != 'gm'){
						uids.add(member.UserId);
					}
				}
				accounts.put(a.Id, uids);
			}
			List<EntitySubscription> followers = [select ParentId, SubscriberId, CreatedById, CreatedBy.Profile.Name from EntitySubscription where ParentId IN: accounts.keySet()];
			List<EntitySubscription> followersToRemove = new List<EntitySubscription>();
			Map<ID, Set<ID>> activefollowers = new Map<ID, Set<ID>>();
			for(EntitySubscription f : followers){
				if(!accounts.get(f.ParentId).contains(f.SubscriberId) && f.CreatedBy.Profile.Name.startsWith('System Administrator') && f.SubscriberId != f.CreatedById){
					followersToRemove.add(f);
				}else{
					Set<ID> fIds;
					if(activefollowers.containsKey(f.ParentId)){
						fIds = activefollowers.get(f.ParentId);
					}else{
						fIds = new Set<ID>();
					}
					fIds.add(f.SubscriberId);
					activefollowers.put(f.ParentId, fIds);
				}
			}
			List<EntitySubscription> followersToAdd = new List<EntitySubscription>();
			for(ID aId : accounts.keySet()){
				Set<ID> memberIds = accounts.get(aId);
				Set<ID> activers = activefollowers.get(aId);
				for(ID mId : memberIds){
					if(activers == null || (activers != null && !activers.contains(mId))){
						EntitySubscription newfollower = new EntitySubscription();
						newfollower.ParentId = aId;
						newfollower.SubscriberId = mId;
						followersToAdd.add(newfollower);
					}
				}
			}
			if(!followersToRemove.isEmpty()){
				delete followersToRemove;
			}  
			if(!followersToAdd.isEmpty()){
				insert followersToAdd;
			}
		}
	}
	
	global void finish(Database.BatchableContext bc){
		if(commandcode == CleanAccountTeam){
			System.debug('Clean finished');
			BatchUpdateSchedulerPart2 part2 = new BatchUpdateSchedulerPart2();
			DateTime times = DateTime.now();
			times = times.addMinutes(3);
			String sch_part2 = times.format('0 mm * * * ?');
			String hhmm = times.format('M/d/20y h:m a');
			System.schedule('Schedule Batch Update Account Team Job part2 ' + hhmm, sch_part2, part2);			
		}else if(commandcode == AggregateAccountTeam){
			System.debug('Aggregate Account Team finished');
			BatchUpdateSchedulerPart3 part3 = new BatchUpdateSchedulerPart3();
			DateTime times = DateTime.now();
			times = times.addMinutes(3);
			String sch_part3 = times.format('0 mm * * * ?');
			String hhmm = times.format('M/d/20y h:m a');
			//System.schedule('Schedule Batch Add and Remove Account followers Job part3 ' + hhmm, sch_part3, part3);				
		}else if(commandcode == AddAndRemoveAccoutFollowersInChatter){ 
			System.debug('Add And Remove Accout Followers In Chatter finished');
		}
		
		System.debug('Batch finished');
	}
		
}